﻿namespace JoinBox.Basal;

using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Text;

// 窗口控件
public partial class WindowsAPI
{
    // 如果 CallBack 返回的是true,则会继续枚举,否则就会终止枚举。
    public delegate bool CallBack(IntPtr hwnd, int lParam);

    /// <summary>
    /// 枚举顶级窗口
    /// </summary>
    /// <param name="lpEnumFunc"></param>
    /// <param name="lParam">要传递给回调函数的应用程序定义的值</param>
    /// <returns></returns>
    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern bool EnumWindows(CallBack lpfn, int lParam);

    /// <summary>
    /// 枚举子窗口所有控件
    /// </summary>
    /// <param name="hWnd">父窗口的句柄,其子窗口将被枚举。如果此参数为NULL,则此函数等效于EnumWindows</param>
    /// <param name="lpfn">指向应用程序定义的回调函数的指针。有关更多信息,请参见EnumChildProc</param>
    /// <param name="lParam">应用程序定义的值,将传递给回调函数</param>
    /// <returns></returns>
    [DllImport("user32.dll")]
    public static extern int EnumChildWindows(IntPtr hWnd, CallBack lpfn, int lParam);

    /// <summary>
    /// 置前窗口
    /// </summary>
    /// <param name="hwnd"></param>
    /// <returns></returns>
    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern bool SetForegroundWindow(IntPtr hwnd);

    /// <summary>
    /// 获取窗口文字
    /// </summary>
    /// <param name="hwnd"></param>
    /// <param name="lpString"></param>
    /// <param name="nMaxCount"></param>
    /// <returns></returns>
    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern int GetWindowText(IntPtr hwnd, [MarshalAs(UnmanagedType.LPWStr)] StringBuilder lpString, int nMaxCount);

    /// <summary>
    /// 设置窗口文字
    /// </summary>
    /// <param name="hwnd"></param>
    /// <param name="lpString"></param>
    /// <returns>失败0</returns>
    [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    public static extern int SetWindowText(IntPtr hwnd, [MarshalAs(UnmanagedType.LPWStr)] StringBuilder lpString);

    /// <summary>
    /// 获取窗口类名
    /// </summary>
    /// <param name="hwnd"></param>
    /// <param name="lpString"></param>
    /// <param name="nMaxCount"></param>
    /// <returns></returns>
    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern int GetClassName(IntPtr hwnd, [MarshalAs(UnmanagedType.LPWStr)] StringBuilder lpString, int nMaxCount);

    /// <summary>
    /// 获取窗口隐藏状态
    /// </summary>
    /// <param name="hwnd">窗口句柄</param>
    /// <returns>如果指定的窗口及其父窗口不具有WS_VISIBLE风格,返回值为零false;
    /// 如果指定的窗口及其父窗口具有WS_VISIBLE风格,返回值为非零true;
    /// 由于返回值表明了窗口是否具有Ws_VISIBLE风格,因此,即使该窗口被其他窗口遮盖,函数返回值也为非零true;
    /// </returns>
    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern bool IsWindowVisible(IntPtr hwnd);

    /// <summary>
    /// 查找子窗口
    /// </summary>
    /// <param name="hwnd">父窗口的句柄,为0则为桌面</param>
    /// <param name="hwndChildAfter">同级第几个窗口开始找,为null则从第一个窗口开始</param>
    /// <param name="lpszClass">指向一个指定了类名的空结束字符串</param>
    /// <param name="lpszWindow">指向一个指定了窗口名（窗口标题）的空结束字符串,null为所有窗口全匹配</param>
    /// <returns>未找到相符窗口返回零</returns>
    [DllImport("user32.dll", EntryPoint = "FindWindowEx", SetLastError = true, CharSet = CharSet.Auto)]
    static extern IntPtr Find(IntPtr hwnd, IntPtr hwndChildAfter, string? lpszClass, string? lpszWindow);


    /// <summary>
    /// 查找子窗口
    /// </summary>
    /// <param name="hwnd">父窗口的句柄,为0则为桌面</param>
    /// <param name="hwndChildAfter">同级第几个窗口开始找,为0则从第一个窗口开始</param>
    /// <param name="lpszClass">指向一个指定了类名的空结束字符串</param>
    /// <param name="lpszWindow">指向一个指定了窗口名（窗口标题）的空结束字符串,null为所有窗口全匹配</param>
    /// <returns>未找到相符窗口返回零</returns>
    public static IntPtr FindWindowEx(IntPtr hwnd, int hwndChildAfter = 0, string? lpszClass = null, string? lpszWindow = null)
    {
        return Find(hwnd, (IntPtr)hwndChildAfter, lpszClass, lpszWindow);
    }

    /// <summary>
    /// 禁止重绘
    /// </summary>
    /// <param name="ctrlHandle">控件句柄</param>
    /// <param name="action"></param>
    public static void SetRedraw(IntPtr ctrlHandle, Action action)
    {
        // 禁止pnl重绘
        // const int WM_SETREDRAW = 0xB;
        SendMessage(ctrlHandle, WM.WM_SETREDRAW, IntPtr.Zero, IntPtr.Zero);
        action();
        // 允许重绘pnl
        SendMessage(ctrlHandle, WM.WM_SETREDRAW, (IntPtr)1, IntPtr.Zero);
    }


    // https://jingyan.baidu.com/article/c45ad29cd5fb58051653e278.html

    /// <summary>
    /// 发送消息
    /// </summary>
    /// <param name="hwnd">窗口句柄</param>
    /// <param name="wMsg">用于区别其他消息的常量值</param>
    /// <param name="wParam">通常是一个与消息有关的常量值,也可能是窗口或控件的句柄</param>
    /// <param name="lParam">通常是一个指向内存中数据的指针</param>
    /// <returns></returns>
    [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    public static extern int SendMessage(IntPtr hwnd, int wMsg, IntPtr wParam, IntPtr lParam);

    [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    public static extern int SendMessage(IntPtr hwnd, WM wMsg, IntPtr wParam, IntPtr lParam);

    /// <summary>
    /// 发送消息
    /// </summary>
    /// <returns></returns>
    [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    public static extern int SendMessage(IntPtr hwnd, int wMsg, IntPtr wParam, StringBuilder lParam);

    /// <summary>
    /// 发送消息
    /// </summary>
    /// <returns></returns>
    [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    public static extern int SendMessage(IntPtr hwnd, int wMsg, IntPtr wParam, ref Rectangle lParam);

    /// <summary>
    /// 发送cad消息
    /// </summary>
    /// <returns></returns>
    [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    public static extern IntPtr SendMessage(IntPtr hWnd, int wMsg, IntPtr wParam, ref CopyDataStruct lParam);

    /// <summary>
    /// 发送信息的结构
    /// </summary>
    public struct CopyDataStruct
    {
        public IntPtr dwData;
        public int cbData;
        public IntPtr lpData;
    }

    /// <summary>
    /// 窗口是否可以被键盘输入,用来检测程序窗口初始化完成
    /// </summary>
    /// <param name="hwnd"></param>
    /// <returns></returns>
    [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    public static extern bool IsWindowEnabled(IntPtr hwnd);



    /// <summary>
    /// 设置窗口样式
    /// </summary>
    /// <param name="hwnd">窗口句柄</param>
    /// <param name="index">为欲获取的信息</param>
    /// <param name="value">值</param>
    /// <returns></returns>
    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern int SetWindowLong(IntPtr hwnd, GWL index, int value);

    /// <summary>
    /// 获取窗口样式
    /// </summary>
    /// <param name="hwnd">窗口句柄</param>
    /// <param name="index">为欲获取的信息</param>
    /// <returns></returns>
    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern int GetWindowLong(IntPtr hwnd, int index);

    /// <summary>
    /// 设置窗口为无焦点
    /// </summary>
    /// <param name="handle">窗口句柄</param>
    public static void SetWindowNoFocus(IntPtr handle)
    {
        // 拓展样式设置-设置窗口为无焦点
        var s2 = GetWindowLong(handle, (int)GWL.GWL_EXSTYLE);
        SetWindowLong(handle, GWL.GWL_EXSTYLE, s2 | (int)WS.WS_EX_NOACTIVATE);
    }
    /// <summary>
    /// 设置窗口为无边框
    /// </summary>
    /// <param name="handle"></param>
    public static void SetWindowNoBorder(IntPtr handle)
    {
        var s2 = (WS)GetWindowLong(handle, (int)GWL.GWL_STYLE);
        s2 &= ~WS.WS_BORDER;
        s2 &= ~WS.WS_THICKFRAME;
        SetWindowLong(handle, GWL.GWL_STYLE, (int)s2);
    }

    /// <summary>
    /// 判断窗口是否为TopMost
    /// </summary>
    /// <param name="hWnd"></param>
    /// <returns></returns>
    public static bool CheckIsTopMost(IntPtr hWnd)
    {
        var top = (int)WS.WS_EX_TOPMOST;
        int style = GetWindowLong(hWnd, top);
        return (style & top) == top;
    }

    [DllImport("user32.dll")]
    public static extern IntPtr GetWindowLongPtr(IntPtr hWnd, int nIndex);

    /// <summary>
    /// 可以修改窗体的最小化最大化
    /// </summary>
    /// <param name="hWnd"></param>
    /// <param name="nCmdShow"></param>
    /// <returns></returns>
    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern int ShowWindow(IntPtr hWnd, NCmdShow nCmdShow);

    /// <summary>
    /// 该函数返回指定窗口的/显示状态/最大化/最小化的窗口位置
    /// </summary>
    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    static extern int GetWindowPlacement(IntPtr hWnd, ref WINDOWPLACEMENT wp);
    public struct WINDOWPLACEMENT
    {
        public int length;
        public uint flags;
        public NCmdShow showCmd;
        public Point ptMinPosition;
        public Point ptMaxPosition;
        public Rectangle rcNormalPosition;
    }

    /// <summary>
    /// 该函数返回指定窗口的/显示状态/最大化/最小化的窗口状态
    /// </summary>
    public static WINDOWPLACEMENT GetWindowPlacement(IntPtr hWnd)
    {
        WINDOWPLACEMENT ws = new();
        ws.length = Marshal.SizeOf(ws);
        WindowsAPI.GetWindowPlacement(hWnd, ref ws);
        return ws;
        /*
        switch (ws.showCmd)
        {
            case 1:// 正常; (int)NCmdShow.SW_SHOWNORMAL
                {
                    System.Windows.Forms.MessageBox.Show("正常");
                    break;
                }
            case 2:// 最小化;   (int)NCmdShow.SW_SHOWMINIMIZED
                {
                    System.Windows.Forms.MessageBox.Show("最小化");

                    break;
                }
            case 3:// 最大化;   (int)NCmdShow.SW_SHOWMAXIMIZED
                {
                    System.Windows.Forms.MessageBox.Show("最大化");

                    break;
                }
        }
        */
    }



    /// <summary>
    /// 根据传入的窗口句柄,获取到同级、顶级、低级的窗口,根据GetWindowCmd 传入的不同获取也不同,详细看GetWindowCmd 类说明
    /// </summary>
    /// <param name="hWnd"></param>
    /// <param name="uCmd"></param>
    /// <returns></returns>
    [DllImport("user32.dll", SetLastError = true)]
    public static extern IntPtr GetWindow(IntPtr hWnd, GetWindowCmd uCmd);

    /// <summary>
    /// 该函数返回桌面窗口的句柄。桌面窗口覆盖整个屏幕。桌面窗口是一个要在其上绘制所有的图标和其他窗口的区域。
    /// 【说明】获得代表整个屏幕的一个窗口（桌面窗口）句柄.
    /// </summary>
    /// <returns>返回值：函数返回桌面窗口的句柄。</returns>
    [DllImport("user32.dll", EntryPoint = "GetDesktopWindow", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern IntPtr GetDesktopWindow();



    // https://www.baidu.com/link?url=sjysQtshWp-Gj8hnhTS1iIqFprchkRbTGYfSLTWv9w2O2aaoMyaTuBp--oEouRSV09jmHHKDg8jIAaFXCL0VYLMvQ_TO4Y3XE2WG085Lhhe&wd=&eqid=9adc7fd70001693f000000065fc4b1a5
    /// <summary>
    /// 结构包含了应用程序在处理WM_NCCALCSIZE消息时可用的信息
    /// (以计算窗口客户区的大小、位置,以及有效内容。)
    /// </summary>
    [StructLayout(LayoutKind.Sequential)]
    public struct NCCALCSIZE_PARAMS
    {
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
        // 矩形数组
        // 第一个矩形包含窗口在移动或改变大小后的新坐标,也就是说,它是建议的新窗口坐标
        // 第二个矩形包含了窗口在移动或改变大小前的坐标
        // 第三个包含了窗口移动或改变大小前的客户区坐标
        // 如果该窗口是子窗口,这些坐标相对于父窗口的客户区。如果窗口是顶层窗口,坐标相对于屏幕原点
        public RECT[] rgrc;
        // 指向WINDOWPOS结构的指针。该结构包含了对窗口进行移动或改变大小的操作时指定的大小和位置
        public WINDOWPOS lppos;
    }

    // https://www.cnblogs.com/goto/archive/2012/07/05/2577696.html
    [StructLayout(LayoutKind.Sequential)]
    public struct WINDOWPOS
    {
        public IntPtr hwnd;// 标识窗口
        public IntPtr hwndInsertAfter;// 标识了一个窗口,本窗口将被放在这个窗口的后面
        public int x;      // 指定了窗口的左边界的位置
        public int y;      // 指定了窗口的右边界的位置
        public int cx;     // 指定了窗口的宽度,以象素为单位
        public int cy;     // 指定了窗口的高度,以象素为单位
        public uint flags; // 指定了窗口位置的选项。这个成员可以是 SWP 枚举 https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-windowpos
    }

    /// <summary>
    /// 设置为子窗体
    /// </summary>
    /// <param name="handle"></param>
    public static void SetWindowLong(IntPtr handle)
    {
        // 窗口是桌面窗口的子窗口,就应在调用SetParent函数之前清空WS_POPUP位并设置WS_CHILD风格
        var s1 = WindowsAPI.GetWindowLong(handle, (int)GWL.GWL_STYLE);
        WindowsAPI.SetWindowLong(handle, GWL.GWL_STYLE, s1 | (int)WS.WS_CHILD);

        // 拓展样式设置-设置窗口为无焦点
        // var s2 = GetWindowLong(handle, (int)GWL.GWL_EXSTYLE);
        // WindowsAPI.SetWindowLong(handle, GWL.GWL_EXSTYLE, s2 | (int)WS.WS_EX_NOACTIVATE);
    }
}